<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>4-组件</title>
		<link rel="stylesheet" href="../../css/css01.css" />
		<script type="text/javascript" src="../../lib/vue/vue.min.js" ></script>

	</head>
	<body class="dbody">
	<h1>1- Vue.js 组件</h1>
	<p>组件（Component）是 Vue.js 最强大的功能之一。</p>
	<p>组件可以扩展 HTML 元素，封装可重用的代码。</p>
	<p>组件系统让我们可以用独立可复用的小组件来构建大型应用，几乎任意类型的应用的界面都可以抽象为一个组件树：</p>
	<h2>1.1- 全局组件</h2>	
	<div id="app" >
		<cp01></cp01>
	</div>	
	<!-- <cp01></cp01> -->
	<script>
		//注册
		Vue.component('cp01',{
			template:'<font color="red">我是全局组件</font>'
		})
		//创建Vue对象
		new Vue({
			el:"#app",
		})
	</script>	
	
	<h2>1.2- 局部组件</h2>	
	<p>只在这个实例中可以使用该自定义组件</p>
	<div id="app2">
		<cp02></cp02>
	</div>
	<script>
		var child = {
			template:"<font color='red'>我是局部组件</font>"
		}
		new Vue({
			el:"#app2",
			components:{
				"cp02":child
			}
		})
	</script>
	
	<h2>1.3 prop传递属性</h2>
	<div id="app3">
		<!--
			父组件的 msg 的输入绑定到子组件的message上，子组件的message因为是模板的属性
			所以绑定在，template定义的中间。
		-->
		<input type="text" v-model="msg" placeholder="..."/><br />
		静态传递prop：<child2 message="hello world!"></child2><br />
		动态传递prop：<child2 v-bind:message="msg"></child2>
	</div>
	<script>
		//注册
		Vue.component('child2',{
			//添加prop
			props:["message"],
			// 同样也可以在 vm 实例中像 "this.message" 这样使用
			template:"<font color='red'>{{message}}</font>"
		})
		new Vue({
			el:"#app3",
			data:{msg:""},
			watch: {
				msg: function(val){
					console.log(val)
				}
			}
		})
	</script>
	<h2>1.3 prop遍历</h2>
	<div id="app4">
		<ol>
			<bianli v-for="item in texts " v-bind:data="item"></bianli>
		</ol>
	</div>
	<script>
		Vue.component("bianli",{
			props:["data"],
			template:"<li><font color='red'>{{data.k}}</font></li>"
		})
		
		new Vue({
			el:"#app4",
			data:{
				texts:[
					{k:"v1"},{k:"v12"},{k:"v3"},
				]
			}
		})
	</script>
	<p><b><font color="red">注意: prop 是单向绑定的：当父组件的属性变化时，将传导给子组件，但是不会反过来。</font></b></p>
	
	<h2>1.4- prop 用作校验 （没有实现）</h2>
	<p> prop验证没有错误信息的可能原因：没有使用开发者版本</p>
	<div id="app5">
		propA<input v-model="inA"><verify01 v-bind:propA="inA"></verify01>左边的输入：{{inA}}<br/>
		propB<input v-model="inB"><verify01 v-bind:propB="inB"></verify01>左边的输入：{{inB}}<br/>
		propC<input v-model="inC"><verify01 v-bind:propC="inC"></verify01>左边的输入：{{inC}}<br/>
		propD<input v-model="inD"><verify01 v-bind:propD="inD"></verify01>左边的输入：{{inD}}<br/>
		propE<input v-model="inE"><verify01 v-bind:propE="inE"></verify01>左边的输入：{{inE}}<br/>
		propF<input v-model="inF"><verify01 v-bind:propF="inF"></verify01>左边的输入：{{inF}}<br/>
	</div>
	<script>
	Vue.component('verify01', {
	    props: {
		    // 基础类型检测 （`null` 意思是任何类型都可以）
		    propA: Number,
		    // 多种类型
		    propB: [String, Number],
		    // 必传且是字符串
		    propC: {
		      type: String,
		      required: true
	    	},
		    // 数字，有默认值
		    propD: {
		      type: Number,
		      default: 100
		    },
		    // 数组／对象的默认值应当由一个工厂函数返回
		    propE: {
		      type: Object,
		      default: function () {
		        return { message: 'hello' }
		      }
		    },
		    // 自定义验证函数
		    propF: {
		      validator: function (value) {
		      	alert(value);
		        return value > 10
		      }
		    }
	  	},
	  	template:"<font color='red'>校验</font>"
	})
	new Vue({
		el:"#app5",
		data:{
			inA:"asd",
			inB:"inB",
			inC:null,
			inD:"inD",
			inE:"inE",
			inF:"inF",
		}
	})
	</script>
	<pre>
		type 可以是下面原生构造器：
		String
		Number
		Boolean
		Function
		Object
		Array
		type 也可以是一个自定义构造器，使用 instanceof 检测。
	</pre>
	
	<h2>自定义事件</h2>
	<pre>父组件是使用 props 传递数据给子组件，但如果子组件要把数据传递回去，就需要使用自定义事件！
		我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface)，即：
		使用 $on(eventName) 监听事件
		使用 $emit(eventName) 触发事件
		另外，父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
		以下实例中子组件已经和它外部完全解耦了。它所做的只是触发一个父组件关心的内部事件。
		
		父子组件传递消息机制：
		1. 子 -> 父：使用$emit(<事件>)
		2. 父 -> 子：使用props绑定父组件的变量值
		
	</pre>
	<div id="app">
		<div id="counter-event-example">
		  <p>{{ total }}</p>
		  <button-counter v-on:increment="incrementTotal('hi',$event)"></button-counter>
		  <button-counter v-on:increment="incrementTotal('hi',$event)"></button-counter>
		  <!-- 既触发时间将子组件的参数传递给父组件,还得到了父组件传递过来的参数 -->
		  <button-counter v-on:increment="incrementTotal('hi',$event)" v-bind:sub_total='total'></button-counter>
		
		  <div>
			  <button @click="eventDemo('OK', $event)" id="click_demo">测试$event</button>
		  </div>
		</div>
		
	</div>

	<script>
	Vue.component('button-counter', {
	  template: '<button v-on:click="increment">点击了第{{ counter }}次,{{sub_total}}</button>',
	  props:['sub_total'],
	  data: function () {
	    return {
	      counter: 0
	    }
	  },
	  methods: {
	    increment: function () {
	      this.counter += 1
	      this.$emit('increment') // 触发该方法是的父组件能够使用v-on监听到
	    }
	  },
	})
	new Vue({
	  el: '#counter-event-example',
	  data: {
	    total: 0
	  },
	  methods: {
		/**
		 * 使用了父子组件的方式获取不到点击事件 e
		 * @param {Object} msg
		 * @param {Object} e
		 */
	    incrementTotal: function (msg, e) {
	    	console.log("msg", msg, e)
	        this.total += 1
	    },
		/**
		 * e.currentTarget是一个瞬时的值，当打印event后，
		 * 等到展开log信息时，冒泡事件已经结束，所以当前
		 * currentTarget是null
		 * @param {Object} v
		 * @param {Object} e
		 */
		eventDemo: function(v, e) {
			// console.log("eventDemo", v, e)
			console.log("eventDemo", v, e.currentTarget)
		}
	  }
	})
	</script>
	<p>如果你想在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on 。例如：</p>
	<pre>
	&lt;my-component v-on:click.native="doTheThing"&gt;&lt;/my-component&gt;
	</pre>
	
	</body>
</html>
